// FabricControls.js
// define a function that can locate the controls.
// this function will be used both for drawing and for interaction.
import { fabric } from 'fabric';
import LabelConfig from '@/js/models/LabelConfig';

function polygonPositionHandler(dim, finalMatrix, fabricObject) {
    var x = fabricObject.points[this.pointIndex].x - fabricObject.pathOffset.x,
        y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y;
    return fabric.util.transformPoint({ x: x, y: y }, fabric.util.multiplyTransformMatrices(fabricObject.canvas.viewportTransform, fabricObject.calcTransformMatrix()));
}

function getObjectSizeWithStroke(object) {
    var stroke = new fabric.Point(object.strokeUniform ? 1 / object.scaleX : 1, object.strokeUniform ? 1 / object.scaleY : 1).multiply(object.strokeWidth);
    return new fabric.Point(object.width + stroke.x, object.height + stroke.y);
}

// define a function that will define what the control does
// this function will be called on every mouse move after a control has been
// clicked and is being dragged.
// The function receive as argument the mouse event, the current trasnform object
// and the current position in canvas coordinate
// transform.target is a reference to the current object being transformed,
function actionHandler(eventData, transform, x, y) {
    var polygon = transform.target,
        currentControl = polygon.controls[polygon.__corner],
        mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
        polygonBaseSize = getObjectSizeWithStroke(polygon),
        size = polygon._getTransformedDimensions(0, 0),
        finalPointPosition = {
            x: (mouseLocalPosition.x * polygonBaseSize.x) / size.x + polygon.pathOffset.x,
            y: (mouseLocalPosition.y * polygonBaseSize.y) / size.y + polygon.pathOffset.y
        };
    polygon.points[currentControl.pointIndex] = finalPointPosition;
    return true;
}

// define a function that can keep the polygon in the same position when we change its
// width/height/top/left.
function anchorWrapper(anchorIndex, fn) {
    return function (eventData, transform, x, y) {
        var fabricObject = transform.target,
            absolutePoint = fabric.util.transformPoint(
                {
                    x: fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x,
                    y: fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y
                },
                fabricObject.calcTransformMatrix()
            ),
            actionPerformed = fn(eventData, transform, x, y),
            newDim = fabricObject._setPositionDimensions({}),
            polygonBaseSize = getObjectSizeWithStroke(fabricObject),
            newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x,
            newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y;
        fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5);
        return actionPerformed;
    };
}

function setPolyEditable(poly) {
    // console.log('setPolyEditable poly = ', poly);
    poly.hasControls = true;
    var lastControl = poly.points.length - 1;
    poly.cornerStyle = 'circle';
    poly.cornerColor = 'rgba(0,0,255,1.0)';
    // poly.transparentCorners = false;
    poly.controls = poly.points.reduce(function (acc, point, index) {
        acc['p' + index] = new fabric.Control({
            positionHandler: polygonPositionHandler,
            actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
            actionName: 'modifyPolygon',
            pointIndex: index
        });
        return acc;
    }, {});
}

function setRectEditable(rect, rotation = true) {
    // console.log('setRectEditable rotation = ', rotation);
    rect.cornerStyle = 'circle';
    rect.cornerColor = 'rgba(0,0,255,1.0)';
    // rect.transparentCorners = false;
    rect.hasControls = true;
    if (!rotation) {
        rect.lockRotation = true;
        rect.setControlsVisibility({
            mtr: false
        });
    }
}

function setCircleEditable(circle) {
    // console.log('setCircleEditable circle = ', circle);
    circle.hasControls = true;
    circle.hasBorders = true; // 显示选中边框
    circle.set({
        cornerColor: 'rgba(0,0,255,1)',      // 控制点颜色
        borderColor: 'rgba(0,0,255,1)',      // 边框颜色
        selectionColor: 'rgba(0,0,255,0.3)',// 选中区域颜色
        selectionBorderColor: 'rgba(0,0,255,1)' // 选中边框颜色
    });
    circle.setControlsVisibility({
        tl: false, // 左上角控制点
        tr: false, // 右上角控制点
        bl: false, // 左下角控制点
        br: false, // 右下角控制点
        ml: false,  // 左侧中点控制点
        mt: false,  // 顶部中点控制点
        mr: false,  // 右侧中点控制点
        mb: false,  // 底部中点控制点
        mtr: false // 旋转控制点
    });
}

function setFObjectEditable(fobject, config) {
    // console.log('setFObjectEditable fobject = ', fobject);
    let { rotation } = config ? config : { rotation: false };
    if (fobject.type == 'polygon') {
        setPolyEditable(fobject);
    } else if (fobject.type == 'rect') {
        setRectEditable(fobject, rotation);
    } else if (fobject.type == 'circle') {
        setCircleEditable(fobject);
    }
}

function unsetFObjectEditable(poly) {
    poly.hasControls = false;
    // poly.cornerColor = 'blue';
    // poly.cornerStyle = 'rect';
    // poly.controls = fabric.Object.prototype.controls;
}

// config是配置的信息
function newPointFObject(label_id, point, custom_config = null) {
    let default_config = LabelConfig.getConfig('point_style');
    let config = custom_config ? custom_config : default_config;

    // 计算点的权重（这里假设point对象包含weight属性）
    const weight = point.weight || 1;

    // 根据权重计算半径
    const minRadius = 5;
    const maxRadius = 30;
    const radius = minRadius + (maxRadius - minRadius) * weight;

    // 创建径向渐变
    const gradient = new fabric.Gradient({
        type: 'radial',
        coords: {
            r1: radius, // 渐变结束半径
            r2: 0,      // 渐变开始半径
            x1: radius, // 渐变中心x（基于对象坐标系，考虑originX=center）
            y1: radius, // 渐变中心y（基于对象坐标系，考虑originY=center）
            x2: radius, // 渐变结束位置x
            y2: radius  // 渐变结束位置y
        },
        colorStops: [
            { offset: 0, color: 'rgba(255, 0, 0, 0.05)' },   // 中心红色
            { offset: 1, color: 'rgba(255, 0, 0, 1)' }  // 外围黄色
        ]
    });

    let pointobj = new fabric.Circle({
        label_id: label_id,
        left: point.left,
        top: point.top,
        radius: radius,
        fill: gradient,
        stroke: 'rgb(255, 174, 0)',
        strokeWidth: 0,
        hasControls: false,
        hasBorders: false,
        objectCaching: false,
        selectable: true,
        evented: true,
        originX: 'center',
        originY: 'center',
        opacity: 0.8,
        shadow: new fabric.Shadow({
            color: 'rgba(155, 155, 155, 0.5)',
            blur: 5 * weight,
            offsetX: 0,
            offsetY: 0
        }),
        data: {
            type: 'heatpoint',
            weight: weight
        }
    });
    return pointobj;
}

function newRectFObject(label_id, rect, custom_config = null) {
    let default_config = LabelConfig.getConfig('rect_style');
    let config = custom_config ? custom_config : default_config;
    let rectobj = new fabric.Rect({
        label_id: label_id,
        left: rect.left,
        top: rect.top,
        width: rect.width,
        height: rect.height,
        fill: config.fill,
        strokeWidth: config.strokeWidth,
        stroke: config.stroke,
        visible: config.visible,
        objectCaching: false,
        hasControls: false,
        hasBorders: false,
        data: {
            type: 'bbox'
        }
    });
    return rectobj;
}

function newPolyFObject(label_id, seg_index, poly, custom_config = null) {
    let default_config = LabelConfig.getConfig('polygon_style');
    let config = custom_config ? custom_config : default_config;
    let polyobj = new fabric.Polygon(poly.points, {
        label_id: label_id,
        seg_index: seg_index,
        left: poly.left,
        top: poly.top,
        fill: config.fill,
        strokeWidth: config.strokeWidth,
        stroke: config.stroke,
        visible: config.visible,
        cornerColor: 'blue',
        objectCaching: false,
        hasControls: false,
        hasBorders: false,
        data: {
            type: 'segm'
        }
    });
    return polyobj;
}

// COCO关键点规范参数说明：
// keypoints: [x1,y1,v1, x2,y2,v2,...] 顺序排列的数组，v为可见性标记：
//   v=0: 未标注
//   v=1: 标注但不可见（遮挡）
//   v=2: 标注且可见
// 解析后的格式
// {
//     x: x,
//     y: y,
//     visibility: visibility, // v=0: 未标注, v=1: 标注但不可见（遮挡）, v=2: 标注且可见
//     id: id
// }
// skeleton: COCO官方骨架连接定义，如 [[16,14], [14,12],...]
function newKeypointsFObject(label_id, keypoints, skeleton, custom_config = null) {
    // 配置继承自COCO规范
    const config = Object.assign({}, custom_config);
    // console.log(`custom_config = `, custom_config, '  config = ', config);

    // 创建关键点对象（过滤未标注点）
    const pointGroup = keypoints
        .filter((kp) => kp.visibility !== 0) // 排除v=0的未标注点
        .map((kp) => {
            return new fabric.Circle({
                label_id: label_id,
                jointId: kp.id,
                left: kp.x,
                top: kp.y,
                radius: config.keypoint.radius,
                fill: getKeypointFill(kp.visibility, config),
                stroke: config.keypoint.stroke,
                strokeWidth: config.keypoint.strokeWidth,
                originX: 'center',
                originY: 'center',
                hasControls: false,
                hasBorders: false,
                hoverCursor: 'pointer',
                selectable: true,
                evented: true,
                data: {
                    type: 'keypoint',
                    visibility: kp.visibility,
                    cocoId: kp.id + 1 // COCO官方ID从1开始
                }
            });
        });

    // 创建COCO规范骨架连接
    const skeletonLines = skeleton
        .filter(([i, j]) => i <= keypoints.length && j <= keypoints.length)
        .map(([i, j]) => {
            const kp1 = keypoints[i - 1];
            const kp2 = keypoints[j - 1];

            // 获取连接线配置
            const lineConfig = {
                stroke: config.skeleton.stroke.default,
                strokeWidth: config.skeleton.strokeWidth,
                lineDash: config.skeleton.lineDash
            };

            // 特定连接样式（例如：12-11连接）
            const skeletonKey = `${i}-${j}`;
            if (config.skeleton.stroke[skeletonKey]) {
                lineConfig.stroke = config.skeleton.stroke[skeletonKey];
            }

            return new fabric.Line([kp1.x, kp1.y, kp2.x, kp2.y], {
                label_id: label_id,
                ...lineConfig,
                strokeLineCap: 'round',
                selectable: false,
                evented: false,
                visible: shouldShowSkeleton(kp1.visibility, kp2.visibility),
                lineIndex: [i - 1, j - 1],// 记录连接的 keypoints 索引
                data: {
                    type: 'skeleton'
                }
            });
        });

    // 计算所有关键点的边界框
    function getBounds(points) {
        let left = Infinity;
        let top = Infinity;
        let right = -Infinity;
        let bottom = -Infinity;

        points.forEach((point) => {
            left = Math.min(left, point.left);
            top = Math.min(top, point.top);
            right = Math.max(right, point.left);
            bottom = Math.max(bottom, point.top);
        });

        return {
            left: left,
            top: top,
            width: right - left,
            height: bottom - top
        };
    }

    // 创建透明的矩形对象用于整体拖动
    const transparentRect = new fabric.Rect({
        label_id: label_id,
        left: 0,
        top: 0,
        width: 0,
        height: 0,
        fill: config.keypoints_back.fill,
        stroke: config.keypoints_back.stroke,
        strokeWidth: config.keypoints_back.strokeWidth,
        selectable: true,
        evented: true,
        hasControls: false,
        hasBorders: false,
        hoverCursor: 'move',
        data: {
            type: 'keypoints_back'
        }
    });

    // 更新透明矩形的位置和大小
    function updateTransparentRect() {
        const bounds = getBounds(pointGroup);
        transparentRect.set({
            left: bounds.left,
            top: bounds.top,
            width: bounds.width,
            height: bounds.height
        });
        transparentRect.setCoords(); // 更新矩形的坐标
    }

    // 初始化透明矩形
    updateTransparentRect();

    // 关键点拖动时更新骨架线和透明矩形
    pointGroup.forEach((point) => {
        point.on('moving', (e) => {
            const jointId = point.jointId;
            const newX = point.left;
            const newY = point.top;

            // 更新所有连接到该关键点的骨架线
            skeletonLines.forEach((line) => {
                const [i, j] = line.lineIndex;
                if (i === jointId || j === jointId) {
                    const kp1 = keypoints[i];
                    const kp2 = keypoints[j];

                    // 更新线的起点或终点
                    if (i === jointId) {
                        line.set({ x1: newX, y1: newY });
                    } else if (j === jointId) {
                        line.set({ x2: newX, y2: newY });
                    }

                    // 更新线的可见性
                    line.set('visible', shouldShowSkeleton(kp1?.visibility, kp2?.visibility));
                }
            });

            // 更新透明矩形的位置和大小
            updateTransparentRect();

            // 强制重新渲染画布
            point.canvas.requestRenderAll();
        });
    });

    // 在透明矩形拖动时更新所有关键点和骨架线的位置
    transparentRect.on('moving', (e) => {
        // console.log('keypoints_back moving, e = ', e);
        const deltaX = transparentRect.left - transparentRect._leftBeforeMove;
        const deltaY = transparentRect.top - transparentRect._topBeforeMove;

        // 统一更新关键点以便动所有对象
        pointGroup.forEach((point) => {
            point.set({
                left: point.left + deltaX,
                top: point.top + deltaY
            });
            point.setCoords(); // 更新坐标
        });

        // 更新所有骨架线的位置
        skeletonLines.forEach((line) => {
            line.set({
                x1: line.x1 + deltaX,
                y1: line.y1 + deltaY,
                x2: line.x2 + deltaX,
                y2: line.y2 + deltaY
            });
            line.setCoords(); // 更新坐标
        });

        // 强制重新渲染画布
        transparentRect.canvas.requestRenderAll();

        // 记录更新后位置
        transparentRect._leftBeforeMove = transparentRect.left;
        transparentRect._topBeforeMove = transparentRect.top;
    });

    // 记录透明矩形的初始位置
    transparentRect.on('mousedown', () => {
        transparentRect._leftBeforeMove = transparentRect.left;
        transparentRect._topBeforeMove = transparentRect.top;
    });

    // 返回包含透明矩形、关键点和骨架线的对象
    return {
        back: transparentRect, // 透明矩形
        kps: pointGroup, // 关键点
        lines: skeletonLines // 骨架线
    };
}

function getKeypointFill(visibility, cfg) {
    return visibility === 2 ? cfg.keypoint.visibleFill : visibility === 1 ? cfg.keypoint.occludedFill : cfg.keypoint.hiddenFill;
}

function shouldShowSkeleton(v1, v2) {
    // COCO规范：只要有一个关键点可见（v≥1）即显示骨架
    return v1 >= 1 && v2 >= 1;
}
export { setFObjectEditable, unsetFObjectEditable, newPointFObject, newRectFObject, newPolyFObject, newKeypointsFObject, getKeypointFill };
